import ast
import random
import json
from mysql_use import mysqlUse

# 生成算式表达式的函数，根据难度级别生成不同规则的算式
def generate_expression(difficulty):
    if difficulty == 1:
        # 简单难度：加减混合，两个运算数在0-100之间
        num1 = random.randint(0, 100)  # 生成第一个随机数
        num2 = random.randint(0, 100)  # 生成第二个随机数
        operator = random.choice(['+', '-'])  # 随机选择加号或减号
        if operator == '-' and num1 < num2:  # 如果是减法且第一个数小于第二个数，则交换两个数
            num1, num2 = num2, num1
        expression = f"{num1} {operator} {num2}"  # 构造算式表达式
    elif difficulty == 2:
        # 中等难度：加减乘除混合，运算数数量为2-4，在0-1000之间
        num_count = random.randint(2, 4)  # 随机生成运算数的数量
        nums = [random.randint(1, 1000) for _ in range(num_count)]  # 生成随机运算数列表，确保不为0
        operators = []  # 存储运算符的列表
        while len(operators) < num_count - 1:  # 循环直到运算符数量达到要求
            op = random.choice(['+', '-', '*', '/'])  # 随机选择一个运算符
            operators.append(op)  # 将运算符添加到列表

        expression = str(nums[0])  # 从第一个数开始构造表达式
        for i in range(len(operators)):  # 遍历运算符列表
            if operators[i] == '*':  # 如果是乘法
                expression += f" {operators[i]} {nums[i + 1]}"
            elif operators[i] == '/': # 如果是除法
                while True:
                    if nums[i + 1] != 0:
                        if nums[i] % nums[i + 1] == 0:
                            break
                    divisor_candidates = [n for n in range(1, nums[i] + 1) if nums[i] % n == 0]
                    next_num = random.choice(divisor_candidates)
                    if nums[i] % next_num == 0:  # 确保除得尽
                        nums[i + 1] = next_num
                        break
                expression += f" {operators[i]} {nums[i + 1]}"
                # 避免下一个操作符也是除法
                if i < len(operators) - 1 and operators[i + 1] == '/':
                    operators[i + 1] = random.choice(['+', '-', '*'])
            else:  # 如果是加法或减法
                while True:
                    next_num = random.randint(1, 1000)  # 生成下一个随机数
                    if (operators[i] == '-' and nums[i] >= next_num) or operators[i] == '+':  # 确保减法的结果非负
                        break
                expression += f" {operators[i]} {next_num}"
    elif difficulty == 3:
        # 困难难度：加减乘除混合，运算数数量为2-9，在0-1000之间
        num_count = random.randint(2, 9)  # 随机生成运算数的数量
        nums = [random.randint(1, 1000) for _ in range(num_count)]  # 生成随机运算数列表，确保不为0
        operators = []  # 存储运算符的列表
        while len(operators) < num_count - 1:  # 循环直到运算符数量达到要求
            op = random.choice(['+', '-', '*', '/'])  # 随机选择一个运算符
            operators.append(op)  # 将运算符添加到列表

        expression = str(nums[0])  # 从第一个数开始构造表达式
        for i in range(len(operators)):  # 遍历运算符列表
            if operators[i] == '*':  # 如果是乘法
                expression += f" {operators[i]} {nums[i + 1]}"
            elif operators[i] == '/':  # 如果是除法
                while True:
                    if nums[i + 1] != 0:
                        if nums[i] % nums[i + 1] == 0:
                            break
                    divisor_candidates = [n for n in range(1, nums[i] + 1) if nums[i] % n == 0]
                    next_num = random.choice(divisor_candidates)
                    if nums[i] % next_num == 0:  # 确保除得尽
                        nums[i + 1] = next_num
                        break
                expression += f" {operators[i]} {nums[i + 1]}"
                # 避免下一个操作符也是除法
                if i < len(operators) - 1 and operators[i + 1] == '/':
                    operators[i + 1] = random.choice(['+', '-', '*'])
            else:  # 如果是加法或减法
                while True:
                    next_num = random.randint(1, 1000)  # 生成下一个随机数
                    if (operators[i] == '-' and nums[i] >= next_num) or operators[i] == '+':  # 确保减法的结果非负
                        break
                expression += f" {operators[i]} {next_num}"
    elif difficulty == 4:
        # 困难难度：加减乘除混合，必须包含括号（仅一对），运算数数量为2-9，在0-1000之间
        num_count = random.randint(4, 9)  # 随机生成运算数的数量，确保至少有4个运算数，以便能够放置一对括号
        nums = [random.randint(1, 1000) for _ in range(num_count)]  # 生成随机运算数列表，确保每个运算数都在1到1000之间
        operators = []  # 初始化一个空列表用于存储运算符
        while len(operators) < num_count - 1:  # 循环直到运算符数量达到要求（运算符数量总是比运算数少1）
            op = random.choice(['+', '-', '*', '/'])  # 随机选择一个运算符（加、减、乘、除）
            operators.append(op)  # 将选择的运算符添加到运算符列表中
        expression_parts = [str(nums[0])]  # 初始化表达式的各个部分，从第一个运算数开始
        # 确定括号的位置，确保括号内至少有一个运算符
        open_pos = random.randint(1, num_count - 2)  # 随机选择开括号的位置，保证其后至少有一个运算符和一个运算数
        close_pos = random.randint(open_pos + 1, num_count - 1)  # 随机选择闭括号的位置，确保括号内的表达式有效
        for i in range(len(operators)):  # 遍历运算符列表，构造完整的表达式
            if operators[i] == '*':  # 如果当前运算符是乘法
                expression_parts.append(f" {operators[i]} {nums[i + 1]}")  # 添加乘法运算符和下一个运算数到表达式部分
            elif operators[i] == '/':  # 如果当前运算符是除法
                while True:  # 确保除法操作是整除
                    if nums[i + 1] != 0 and nums[i] % nums[i + 1] == 0:  # 检查除数不为零且能整除
                        break
                    divisor_candidates = [n for n in range(1, nums[i] + 1) if nums[i] % n == 0]  # 获取所有可能的除数
                    next_num = random.choice(divisor_candidates)  # 随机选择一个合适的除数
                    if nums[i] % next_num == 0:  # 再次确认是否能整除
                        nums[i + 1] = next_num  # 更新运算数列表中的除数
                        break
                expression_parts.append(f" {operators[i]} {nums[i + 1]}")  # 添加除法运算符和更新后的运算数到表达式部分
                # 避免连续出现两个除法运算符
                if i < len(operators) - 1 and operators[i + 1] == '/':
                    operators[i + 1] = random.choice(['+', '-', '*'])  # 将下一个运算符替换为非除法运算符
            else:  # 如果当前运算符是加法或减法
                while True:  # 确保减法结果非负
                    next_num = random.randint(1, 1000)  # 生成下一个随机数作为下一个运算数
                    if (operators[i] == '-' and nums[i] >= next_num) or operators[i] == '+':  # 确保减法的结果非负或直接添加加法
                        break
                expression_parts.append(f" {operators[i]} {next_num}")  # 添加运算符和运算数到表达式部分
        # 构建带括号的表达式
        part_before_open = ' '.join(expression_parts[:open_pos])  # 获取括号前的部分表达式
        part_before_open=part_before_open.split(" ")
        num=part_before_open[-1]
        part_before_open =part_before_open[:-1]
        part_inside_parentheses = num+' '.join(expression_parts[open_pos:close_pos + 1])  # 获取括号内的部分表达式
        part_after_close = ' '.join(expression_parts[close_pos + 1:]) if close_pos + 1 < len(expression_parts) else ''  # 获取括号后的部分表达式
        part_before_open=' '.join(part_before_open)
        expression = f"{part_before_open} ( {part_inside_parentheses} ) {part_after_close}".strip()  # 组合三部分形成最终表达式，并去除多余的空格

    return expression  # 返回构建好的表达式

# 计算表达式结果的函数，确保结果不为负且不出现复数
def evaluate_expression(expression):
    try:
        result = eval(expression)  # 计算表达式的值
        if isinstance(result, complex) or result < 0:  # 检查结果是否为复数或负数
            return None
        return int(result)  # 返回结果，保留两位小数
    except ZeroDivisionError:  # 捕获除以零的错误
        return None
    except Exception as e:
        print(f"错误的表达式：'{expression}': {e}")
        return None

# 检查算式的合法性
def is_valid_expression(expression):
    try:
        # 解析表达式为AST树，mode='eval'表示解析为一个简单的表达式
        tree = ast.parse(expression, mode='eval')
        # 遍历AST树中的所有节点
        for node in ast.walk(tree):
            # 如果节点是二元运算节点（例如加法、减法等）
            if isinstance(node, ast.BinOp):
                # 递归计算左操作数的值
                left_val = eval_node(node.left)
                # 递归计算右操作数的值
                right_val = eval_node(node.right)
                # 获取操作符的类型
                op_type = type(node.op).__name__
                # 如果操作符是减法
                if op_type == 'Sub':
                    # 检查左操作数是否小于等于右操作数，如果是，则表达式不合法
                    if left_val <= right_val:
                        return False
                elif op_type == 'Mult':
                    # 检查左操作数是否等于右操作数，如果是，则表达式不合法
                    if left_val == right_val:
                        return False
                # 如果操作符是除法
                elif op_type == 'Div':
                    # 检查左操作数除以右操作数的余数是否不为0，如果有余数，则表达式不合法
                    if left_val % right_val != 0:
                        return False
        # 如果所有检查通过，则表达式合法
        return True
    except Exception as e:
        # 如果在解析或计算过程中出现任何异常，打印错误信息并返回False
        print(f"异常：'{expression}': {e}")
        return False

# 递归计算AST节点的值
def eval_node(node):
    # 如果节点是二元运算节点
    if isinstance(node, ast.BinOp):
        # 递归计算左操作数的值
        left = eval_node(node.left)
        # 递归计算右操作数的值
        right = eval_node(node.right)
        # 获取操作符的类型
        op_type = type(node.op).__name__
        # 根据操作符类型进行相应的运算
        if op_type == 'Add':
            return left + right
        elif op_type == 'Sub':
            return left - right
        elif op_type == 'Mult':
            return left * right
        elif op_type == 'Div':
            # 如果右操作数为0，抛出除以0的异常
            if right == 0:
                raise ZeroDivisionError("division by zero")
            return left / right
    # 如果节点是数字节点（Python 3.7及以前）
    elif isinstance(node, ast.Num):
        return node.n
    # 如果节点是常量节点（Python 3.8+）
    elif isinstance(node, ast.Constant):
        return node.value
    else:
        # 如果遇到不支持的操作，抛出异常
        raise TypeError(f"Unsupported operation: {ast.dump(node)}")

# 生成一系列算式的函数
def generate_calculations(count=100):
    calculations = []  # 存储生成的算式
    calc_id = 301  # 算式的唯一标识符
    difficulties = [1, 2, 3, 4]  # 难度等级列表
    generated_expressions = set()  # 用于存储已生成的算式表达式

    while len(calculations) < count:  # 循环直到生成足够数量的算式
        # difficulty = random.choice(difficulties)  # 随机选择一个难度等级
        difficulty = 4
        expression = generate_expression(difficulty)  # 生成算式表达式
        if expression.startswith('(') or expression.endswith(')'):
            continue
        # 检查生成的表达式是否已经存在
        if expression in generated_expressions:
            continue

        # 检查生成的表达式是否合法
        if not is_valid_expression(expression):
            continue

        answer = evaluate_expression(expression)  # 计算算式的结果
        if answer is not None:  # 如果结果是有效的
            calculations.append({  # 将算式信息添加到列表
                'calc_id': calc_id,
                'difficulty': difficulty,
                'expression': expression,
                'answer': answer
            })
            generated_expressions.add(expression)  # 将表达式加入已生成集合
            calc_id += 1  # 增加算式标识符
    return calculations

def save_to_file(calculations, filename='calculations.json'):
    with open(filename, 'w') as file:  # 打开文件进行写入
        json.dump(calculations, file, indent=4)  # 将算式信息以JSON格式写入文件

# 程序的主入口
if __name__ == "__main__":
    calculations = generate_calculations()  # 生成算式
    save_to_file(calculations)  # 将算式保存到文件
    db = mysqlUse()
    for calculation in calculations:
        calc_id=calculation['calc_id']
        # print(calculation['calc_id'])
        difficulty=calculation['difficulty']
        expression=calculation['expression']
        answer=calculation['answer']
        db.add_calculations(calc_id, difficulty, expression, answer)
